home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / rc-1.000 / rc-1 / rc-1.5-linux / glob.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-07  |  6.6 KB  |  271 lines

  1. /* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
  2.  
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include "rc.h"
  6. #ifdef NODIRENT
  7. #include <sys/dir.h>
  8. #define dirent direct /* need to get the struct declaraction right */
  9. #else
  10. #include <dirent.h>
  11. #endif
  12.  
  13. static List *dmatch(char *, char *, char *);
  14. static List *doglob(char *, char *);
  15. static List *lglob(List *, char *, char *, SIZE_T);
  16. static List *sort(List *);
  17.  
  18. /*
  19.    Matches a list of words s against a list of patterns p. Returns true iff
  20.    a pattern in p matches a word in s. () matches (), but otherwise null
  21.    patterns match nothing.
  22. */
  23.  
  24. extern bool lmatch(List *s, List *p) {
  25.     List *q;
  26.     int i;
  27.     bool okay;
  28.     if (s == NULL) {
  29.         if (p == NULL) /* null matches null */
  30.             return TRUE;
  31.         for (; p != NULL; p = p->n) { /* one or more stars match null */
  32.             if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */
  33.                 okay = TRUE;
  34.                 for (i = 0; p->w[i] != '\0'; i++)
  35.                     if (p->w[i] != '*' || p->m[i] != 1) {
  36.                         okay = FALSE;
  37.                         break;
  38.                     }
  39.                 if (okay)
  40.                     return TRUE;
  41.             }
  42.         }
  43.         return FALSE;
  44.     }
  45.     for (; s != NULL; s = s->n)
  46.         for (q = p; q != NULL; q = q->n)
  47.             if (match(q->w, q->m, s->w))
  48.                 return TRUE;
  49.     return FALSE;
  50. }
  51.  
  52. /*
  53.    Globs a list; checks to see if each element in the list has a metacharacter. If it
  54.    does, it is globbed, and the output is sorted.
  55. */
  56.  
  57. extern List *glob(List *s) {
  58.     List *top, *r;
  59.     bool meta;
  60.     for (r = s, meta = FALSE; r != NULL; r = r->n)
  61.         if (r->m != NULL)
  62.             meta = TRUE;
  63.     if (!meta)
  64.         return s; /* don't copy lists with no metacharacters in them */
  65.     for (top = r = NULL; s != NULL; s = s->n) {
  66.         if (s->m == NULL) { /* no metacharacters; just tack on to the return list */
  67.             if (top == NULL)
  68.                 top = r = nnew(List);
  69.             else
  70.                 r = r->n = nnew(List);
  71.             r->w = s->w;
  72.         } else {
  73.             if (top == NULL)
  74.                 top = r = sort(doglob(s->w, s->m));
  75.             else
  76.                 r->n = sort(doglob(s->w, s->m));
  77.             while (r->n != NULL)
  78.                 r = r->n;
  79.         }
  80.     }
  81.     r->n = NULL;
  82.     return top;
  83. }
  84.  
  85. /* Matches a pattern p against the contents of directory d */
  86.  
  87. static List *dmatch(char *d, char *p, char *m) {
  88.     bool matched = FALSE;
  89.     List *top, *r;
  90.     static DIR *dirp;
  91.     static struct dirent *dp;
  92.     static struct stat s;
  93.     /* prototypes for XXXdir functions. comment out if necessary */
  94.     extern DIR *opendir(const char *);
  95.     extern struct dirent *readdir(DIR *);
  96.     /*extern int closedir(DIR *);*/
  97.     int i;
  98.  
  99.     /*
  100.         return a match if there are no metacharacters; allows globbing through
  101.        directories with no read permission. make sure the file exists, though.
  102.      */
  103.     matched = TRUE;
  104.     if (m != NULL)
  105.         for (i = 0; p[i] != '\0'; i++)
  106.             if (m[i]) {
  107.                 matched = FALSE;
  108.                 break;
  109.             }
  110.  
  111.     if (matched) {
  112.         char *path = nprint("%s/%s", d, p);
  113.         if (stat(path, &s) < 0)
  114.             return NULL;
  115.         r = nnew(List);
  116.         r->w = ncpy(p);
  117.         r->m = NULL;
  118.         r->n = NULL;
  119.         return r;
  120.     }
  121.  
  122.     top = r = NULL;
  123.     if ((dirp = opendir(d)) == NULL)
  124.         return NULL;
  125.     /* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */
  126.     if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR) {
  127.         closedir(dirp);
  128.         return NULL;
  129.     }
  130.     while ((dp = readdir(dirp)) != NULL)
  131.         if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */
  132.             matched = TRUE;
  133.             if (top == NULL)
  134.                 top = r = nnew(List);
  135.             else
  136.                 r = r->n = nnew(List);
  137.             r->w = ncpy(dp->d_name);
  138.             r->m = NULL;
  139.         }
  140.     closedir(dirp);
  141.     if (!matched)
  142.         return NULL;
  143.     r->n = NULL;
  144.     return top;
  145. }
  146.  
  147. /*
  148.    lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
  149.    will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
  150.    slashcount indicates the number of slashes to stick between the directory and the
  151.    matched name. e.g., for matching ////tmp/////foo*
  152. */
  153.  
  154. static List *lglob(List *s, char *p, char *m, SIZE_T slashcount) {
  155.     List *q, *r, *top, foo;
  156.     static struct {
  157.         List l;
  158.         SIZE_T size;
  159.     } slash;
  160.     if (slashcount+1 > slash.size) {
  161.         slash.size = 2*(slashcount+1);
  162.         slash.l.w = erealloc(slash.l.w, slash.size);
  163.     }
  164.     slash.l.w[slashcount] = '\0';
  165.     while (slashcount > 0)
  166.         slash.l.w[--slashcount] = '/';
  167.     for (top = r = NULL; s != NULL; s = s->n) {
  168.         q = dmatch(s->w, p, m);
  169.         if (q != NULL) {
  170.             foo.w = s->w;
  171.             foo.m = NULL;
  172.             foo.n = NULL;
  173.             if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */
  174.                 q = concat(&slash.l, q);      /* dir/name with slash */
  175.             q = concat(&foo, q);
  176.             if (r == NULL)
  177.                 top = r = q;
  178.             else
  179.                 r->n = q;
  180.             while (r->n != NULL)
  181.                 r = r->n;
  182.         }
  183.     }
  184.     return top;
  185. }
  186.  
  187. /*
  188.    Doglob globs a pathname in pattern form against a unix path. Returns the original
  189.    pattern (cleaned of metacharacters) on failure, or the globbed string(s).
  190. */
  191.  
  192. static List *doglob(char *w, char *m) {
  193.     static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL;
  194.     static SIZE_T dsize = 0;
  195.     char *d, *p, *md, *mp;
  196.     SIZE_T psize;
  197.     char *s = w;
  198.     List firstdir;
  199.     List *matched;
  200.     if ((psize = strlen(w) + 1) > dsize || dir == NULL) {
  201.         efree(dir); efree(pattern); efree(metadir); efree(metapattern);
  202.         dir = ealloc(psize);
  203.         pattern = ealloc(psize);
  204.         metadir = ealloc(psize);
  205.         metapattern = ealloc(psize);
  206.         dsize = psize;
  207.     }
  208.     d = dir;
  209.     p = pattern;
  210.     md = metadir;
  211.     mp = metapattern;
  212.     if (*s == '/')
  213.         while (*s == '/')
  214.             *d++ = *s++, *md++ = *m++;
  215.     else
  216.         while (*s != '/' && *s != '\0')
  217.             *d++ = *s++, *md++ = *m++; /* get first directory component */
  218.     *d = '\0';
  219.     /*
  220.        Special case: no slashes in the pattern, i.e., open the current directory.
  221.        Remember that w cannot consist of slashes alone (the other way *s could be
  222.        zero) since doglob gets called iff there's a metacharacter to be matched
  223.     */
  224.     if (*s == '\0') {
  225.         matched = dmatch(".", dir, metadir);
  226.         goto end;
  227.     }
  228.     if (*w == '/') {
  229.         firstdir.w = dir;
  230.         firstdir.m = metadir;
  231.         firstdir.n = NULL;
  232.         matched = &firstdir;
  233.     } else {
  234.         /*
  235.            we must glob against current directory,
  236.            since the first character is not a slash.
  237.         */
  238.         matched = dmatch(".", dir, metadir);
  239.     }
  240.     do {
  241.         SIZE_T slashcount;
  242.         sigchk();
  243.         for (slashcount = 0; *s == '/'; s++, m++)
  244.             slashcount++; /* skip slashes */
  245.         while (*s != '/' && *s != '\0')
  246.             *p++ = *s++, *mp++ = *m++; /* get pattern */
  247.         *p = '\0';
  248.         matched = lglob(matched, pattern, metapattern, slashcount);
  249.         p = pattern, mp = metapattern;
  250.     } while (*s != '\0');
  251. end:    if (matched == NULL) {
  252.         matched = nnew(List);
  253.         matched->w = w;
  254.         matched->m = NULL;
  255.         matched->n = NULL;
  256.     }
  257.     return matched;
  258. }
  259.  
  260. static List *sort(List *s) {
  261.     SIZE_T nel = listnel(s);
  262.     if (nel > 1) {
  263.         char **a;
  264.         List *t;
  265.         qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp);
  266.         for (t = s; t != NULL; t = t->n)
  267.             t->w = *a++;
  268.     }
  269.     return s;
  270. }
  271.